﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GN.Framework.Domain.Repositories.QueryProtocol;
using GN.Pay.Application.Dto.Input;
using GN.Pay.Application.MerchantApp.Dto.Input;
using GN.Pay.Application.MerchantApp.Dto.Output;
using GN.Pay.Core.Repositories;
using Abp.AutoMapper;
using GN.Pay.Core.Entities;
using GN.Pay.Core.Services;
using System.Security.Cryptography;
using GN.Pay.Utils;
using GN.Pay.Core.Runtime.Caching;
using GN.Pay.Core.Runtime;
using GN.Pay.Core.Tasks.Queue;

namespace GN.Pay.Application.MerchantApp.Impl
{
    /// <summary>
    /// 
    /// </summary>
    public class MerchantAppInfoAppService : MerchantAppService, IMerchantAppInfoAppService
    {
        private readonly IMerchantAppInfoRepository repository;
        private readonly IMerchantInfoRepository merchantRepository;
        private readonly IGenerateIdentifier generateIdentifier;
        private readonly IAppSecurity appSecurity;
        private readonly ITradeCacheService tradeCacheService;
        private readonly IQueueManager queueManager;

        public MerchantAppInfoAppService(IMerchantAppInfoRepository repository,
            IMerchantInfoRepository merchantRepository,
            IGenerateIdentifier generateIdentifier,
            IAppSecurity appSecurity,
            ITradeCacheService tradeCacheService,
            IQueueManager queueManager)
        {
            this.repository = repository;
            this.merchantRepository = merchantRepository;
            this.generateIdentifier = generateIdentifier;
            this.appSecurity = appSecurity;
            this.tradeCacheService = tradeCacheService;
            this.queueManager = queueManager;
        }


        private void Encrypt(MerchantAppInfo entity)
        {
            var info = appSecurity.Encrypt(entity);
            entity.PrivateKey = info.PrivateKey;
            entity.PublicKey = info.PublicKey;
            entity.SignKey = info.SignKey;
            entity.AppKey = info.AppKey;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public MerchantAppInfoAddOutput Add(MerchantAppInfoAddInput input)
        {
            var entity = input.MapTo<MerchantAppInfo>();
            //临时，实际需要从会话中读取
            var m = merchantRepository.GetAll().FirstOrDefault(s => s.Id == input.MerchantId);
            if (m == null)
            {
                throw new Abp.UI.UserFriendlyException("商户不存在");
            }
            if (repository.Count(s => s.MerchantId == m.Id && s.AppName == entity.AppName) > 0)
            {
                throw new Abp.UI.UserFriendlyException("同一商户的应用名称不能重复");
            }
            entity.MerchantId = m.Id;
            entity.Id = generateIdentifier.GenerateAppId(entity.MerchantId, entity.AppName);
            entity.State = 1;
            Encrypt(entity);
            repository.Insert(entity);
            var result = entity.MapTo<MerchantAppInfoAddOutput>();
            var app = appSecurity.Decrypt(entity);
            result.AppKey = app.AppKey;
            return result;
        }

        /// <summary>
        /// 修改应用
        /// </summary>
        /// <param name="input">输入</param>
        /// <returns></returns>
        public MerchantAppInfoOutput Modify(MerchantAppInfoModifyInput input)
        {
            var entity = repository.Get(input.Id);
            if (entity == null)
            {
                throw new Abp.UI.UserFriendlyException("无法修改不存在的应用");
            }
            if (!entity.AppName.Equals(input.AppName))
            {
                if (repository.Count(s => s.MerchantId == entity.MerchantId && s.AppName == input.AppName) > 0)
                {
                    throw new Abp.UI.UserFriendlyException("同一商户的应用名称不能重复");
                }
            }

            //检查商家
            entity.AppName = input.AppName;
            entity.AppHomeUrl = input.AppHomeUrl;
            entity.PayDefaultNotifyUrl = input.PayDefaultNotifyUrl;
            entity.PayDefaultReturnUrl = input.PayDefaultReturnUrl;
            entity.RefundDefaultNotifyUrl = input.RefundDefaultNotifyUrl;

            tradeCacheService.RemoveMerchantAppInfo(entity.Id);

            repository.Update(entity);

            return entity.MapTo<MerchantAppInfoOutput>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="option"></param>
        /// <returns></returns>
        public PageQueryResult<MerchantAppInfoOutput> List(PageQueryOption option)
        {
            var query = repository.CreateQuery();
            var result = query.SearchBy(s => s.AppName)
                        .SearchBy(s => s.Id)
                        .Where(option)
                        .OrderBy(s => s.Id)
                        .ToPage(option.CurrentPage, 50);
            var items = result.Items.MapTo<List<MerchantAppInfoOutput>>();
            return new PageQueryResult<MerchantAppInfoOutput>(result.RowCount, result.PageSize, result.CurrentPage, items);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public string QueryAppKey(PrimaryKeyInput<string> input)
        {
            var entity = repository.Get(input.Id);
            if (entity == null)
            {
                throw new Abp.UI.UserFriendlyException("指定的应用不存在.");
            }
            var info = appSecurity.Decrypt(entity);
            //检查商户
            return info.AppKey;
        }

        /// <summary>
        /// 更新应用Key
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public string UpdateAppKey(PrimaryKeyInput<string> input)
        {
            var entity = repository.Get(input.Id);
            if (entity == null)
            {
                throw new Abp.UI.UserFriendlyException("指定的应用不存在.");
            }
            var info = appSecurity.Encrypt(entity);
            entity.AppKey = info.AppKey;
            tradeCacheService.RemoveMerchantAppInfo(entity.Id);
            repository.Update(entity);
            var app = appSecurity.Decrypt(entity);
            return app.AppKey;
        }
    }
}
